/**
 *  JFTSTool - Java Full Text Search tool.
 *  Copyright (C) <2009>  <grupo JFTS>
 *  This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jftstool.fileParserFramework.languajeDetector;

import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import net.olivo.lc4j.LanguageCategorization;

import org.jftstool.config.JFTSConfiguration;
import org.jftstool.config.LanguageDetectorConfig;
import org.jftstool.config.exceptions.JFTSConfigException;
import org.jftstool.fileParserFramework.exceptions.LanguageDetectorException;

/**
 * Class designed to detect the language of a document.
 * @author grupo JFTS
 *
 */
public class LanguageDetector {
	private LanguageDetectorConfig config = null;
	private LanguageCategorization lc = null;
	private List<String> supportedLanguages = new ArrayList<String>();
	private int buffer_size = 0;
	private String default_encode = null;
	private String unknown_language = null;
	private static LanguageDetector instance = null;
	
	
	public static LanguageDetector getInstance() throws LanguageDetectorException {
		if (instance == null){
			instance = new LanguageDetector(); 
		}
		return instance;
	}
	
	private LanguageDetector() throws LanguageDetectorException {
		lc = new LanguageCategorization();
		try {
			JFTSConfiguration mainConfig = JFTSConfiguration.getInstance();
			config = mainConfig.getLanguageDetectorConfig();
			_loadSupportedLanguages();
			
			lc.setLanguageModelsDir(config.getLanguageModelsDir());
			lc.setMaxLanguages(config.getMaxLanguages());
			lc.setNumCharsToExamine(config.getNumCharsToExamine());
			lc.setUnknownThreshold(config.getUnknownThreshold());
			lc.setUseTopmostNgrams(config.getUseTopmostNgrams());			
			buffer_size = config.getBufferSize();
			default_encode = config.getDefaultEncode();
			unknown_language = config.getUnknownLanguage();
		}
		catch (JFTSConfigException e) {
			throw new LanguageDetectorException(e.getMessage());
		}
	}

	
	/**
	 * Returns the language of the document received as InputStream.
	 * @param is
	 * @return String
	 * @throws LanguageDetectorException
	 */
	public String getLanguage(InputStream is) throws LanguageDetectorException {
		ByteArrayList input = new ByteArrayList();
		DataInputStream dis = null;
		List<?> languages = null;
		dis = new DataInputStream(new FastBufferedInputStream(is , buffer_size));
		try {
			while (true){
				input.add(dis.readByte());
			}
		}
		catch (EOFException e){
			/*do nothing*/
		}
		catch (IOException e){
			try {
				dis.close();
			}
			catch (IOException e1) { /*do nothing*/ }
		}
		languages = lc.findLanguage(input);
		return _analyzeResult(languages);
	}
	
	
	/**
	 * Returns the language of the document received as file.
	 * @param file
	 * @return String
	 * @throws LanguageDetectorException
	 */
	public String getLanguage(File file) throws LanguageDetectorException {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(file);
		}
		catch (FileNotFoundException e) {
			throw new LanguageDetectorException("File no't found: " + file.getAbsolutePath());
		} 
		return this.getLanguage(fis);
	}	

	
	/**
	 * Returns the language of the document received as String.
	 * @param text
	 * @return String
	 * @throws LanguageDetectorException
	 */
	public String getLanguage(String text) throws LanguageDetectorException {
		String language = null;
		text = (lc.getNumCharsToExamine() != 0) ? text.substring(0, (lc.getNumCharsToExamine() < text.length()) ? lc.getNumCharsToExamine() : text.length()) : text;
		try {
			InputStream is = new ByteArrayInputStream(text.getBytes(default_encode));
			language = this.getLanguage(is);
		}
		catch (UnsupportedEncodingException e) {
			throw new LanguageDetectorException("Error, unsupported encoding");			
		} 		
		return language;
	}
	
	
	private String _analyzeResult(List<?> languages){
		String returnLanguage = unknown_language;
		boolean find = false;
		
		Iterator<String> it = supportedLanguages.iterator();
		Iterator<?> it1 = languages.iterator();		
		
		while(it.hasNext() && !find){
			String  supportedLanguage = it.next();
			while(it1.hasNext() && !find){
				String language =  _getLanguageName((String) it1.next());
				if (language.trim().contains(supportedLanguage)){
					returnLanguage = supportedLanguage;					
					find = true;
				}
			}
			it1 = languages.iterator();
		}
		return returnLanguage;
	}
	
	
	private String _getLanguageName(String language){
		String aux = "";		
		if (language != null){
			int index = language.indexOf(".");
			if ((index > 0) && (index < language.length())) {
				aux = language.substring(0, index);
			}			
		}
		return aux; 
	}	
	
	
	private void _loadSupportedLanguages() throws LanguageDetectorException{
		try {
			JFTSConfiguration mainConfig = JFTSConfiguration.getInstance();
			supportedLanguages = Arrays.asList(mainConfig.getSupportedLanguages());			
		}
		catch (JFTSConfigException e) {
			throw new LanguageDetectorException(e.getMessage());
		}
	} 
}